#include <jni.h>
#include <string>
#include <android/log.h>

#define TAG    "jasonwan" // 这个是自定义的LOG的标识
#define LOGD(...)  __android_log_print(ANDROID_LOG_DEBUG,TAG,__VA_ARGS__) // 定义LOGD类型
#define LOGI(...)  __android_log_print(ANDROID_LOG_INFO,TAG,__VA_ARGS__) // 定义LOGI类型
#define LOGW(...)  __android_log_print(ANDROID_LOG_WARN,TAG,__VA_ARGS__) // 定义LOGW类型
#define LOGE(...)  __android_log_print(ANDROID_LOG_ERROR,TAG,__VA_ARGS__) // 定义LOGE类型

/**
 * 尽管java中的stringFromJNI()方法没有参数，但cpp中仍然有两个参数，
 * 参数一：JNIEnv* env表示指向可用JNI函数表的接口指针
 * 参数二：jobject是调用该方法的java对象
 */
extern "C"
JNIEXPORT jstring JNICALL Java_com_jason_jni_MainActivity_stringFromJNI(
        JNIEnv *env,
        jobject /* this */) {
    std::string hello = "Hello from C++";
    return env->NewStringUTF(hello.c_str());
}

extern "C"
JNIEXPORT jint JNICALL Java_com_jason_jni_MainActivity_getAge(JNIEnv *env, jobject thiz) {
    //获取java类的实例对象
    jclass clazz = env->GetObjectClass(thiz);
    //判断thiz是否为jclass类型
    jboolean result = env->IsInstanceOf(thiz, clazz);
    LOGD("jni->result=%d", result);
    return 1;
}

extern "C"
JNIEXPORT void JNICALL
Java_com_jason_jni_MainActivity_jniTest(JNIEnv *env, jobject thiz) {
    //获取java类的实例对象
    jclass clazz = env->GetObjectClass(thiz);
    //获取MainActivity中num变量
    jfieldID numFieldId = env->GetFieldID(clazz, "num", "I");
    jint oldValue = env->GetIntField(thiz, numFieldId);
    //将num变量的值+1
    env->SetIntField(thiz, numFieldId, oldValue + 1);
    //重新获取num
    jint num = env->GetIntField(thiz, numFieldId);
    //获取tv控件对象
    jfieldID tvFieldId = env->GetFieldID(clazz, "tv", "Landroid/widget/TextView;");
    jobject tvObject = env->GetObjectField(thiz, tvFieldId);
    jclass tvClass = env->GetObjectClass(tvObject);
    //获取setText方法ID
    jmethodID methodId = env->GetMethodID(tvClass, "setText", "([CII)V");
    //获取setText所需的参数
    //先将num转化为jstring
    char buf[64];
    sprintf(buf, "%d", num);
    jstring pJstring = env->NewStringUTF(buf);
    const char *value = env->GetStringUTFChars(pJstring, JNI_FALSE);
    //创建char数组，长度为字符串num的长度
    jcharArray charArray = env->NewCharArray(strlen(value));
    //开辟jchar内存空间
    jchar *pArray = (jchar *) calloc(strlen(value), sizeof(jchar));
    //将num的值缓冲到内存空间中
    for (int i = 0; i < strlen(value); ++i) {
        *(pArray + i) = *(value + i);
    }
    //将缓冲的值写入到char数组中
    env->SetCharArrayRegion(charArray, 0, strlen(value), pArray);
    //调用setText方法
    env->CallVoidMethod(tvObject, methodId, charArray, 0, env->GetArrayLength(charArray));
    //释放资源
    env->ReleaseCharArrayElements(charArray, env->GetCharArrayElements(charArray, JNI_FALSE), 0);
    free(pArray);
    pArray = NULL;
}

extern "C"
JNIEXPORT void JNICALL
Java_com_jason_jni_MainActivity_jniTest2(JNIEnv *env, jobject thiz, jobject person) {


    jclass j_class = env->GetObjectClass(person);
    jmethodID j_method = env->GetMethodID(j_class, "getName", "()Ljava/lang/String;");

    jthrowable throwable = env->ExceptionOccurred();
    if (throwable) {
        LOGD("normal");
        env->ExceptionClear();
        //异常处理方式1：往java层抛出异常
        jclass exceptionClz = env->FindClass("java/lang/IllegalArgumentException");
        env->ThrowNew(exceptionClz, "can not find method getName2");
        //env->Throw(throwable);
        LOGD("occur a error1");
        //抛完异常后必须清楚异常，否则会导致VM崩溃

        //异常处理方式2：清理异常，类似try-catch
        //env->ExceptionClear();
        //LOGD("occur a error2");
        return;
    }

    jstring name = (jstring) env->CallObjectMethod(person, j_method);
    if (throwable) {
        env->ExceptionClear();
        LOGD("occur a error 3");
    }
    LOGD("ok");
    const char *nameString = env->GetStringUTFChars(name, JNI_FALSE);
    jsize nameSize = env->GetStringLength(name);
    LOGD("the name is %s, the name size is %d", nameString, nameSize);
}